library(tidyverse)
library(lubridate)
library(rvest)
library(janitor)
Se importa la tabla “Compustat Global Daily” con los tipos de datos correctos.
global_daily <- read_csv("Compustat_Global_Daily.csv",
col_types = cols(
sedol = col_character(),
datadate = col_date(format = "%Y%m%d")
)
)
Extraemos de Wikipedia la tabla de códigos GICS con sus respectivos nombres.
gics_table <- read_html("https://en.wikipedia.org/wiki/Global_Industry_Classification_Standard") %>%
html_node("table") %>%
html_table(fill = TRUE) %>%
as_tibble(.name_repair = "universal") %>%
distinct(gsector = Sector...1, gics_name = Sector...2)
Se le añaden los nombres de los sectores a la tabla original
global_daily <- left_join(global_daily, gics_table, by = "gsector")
Se añade una variable para identicar de manera única a cada registro.
global_daily <- global_daily %>%
arrange(datadate) %>%
mutate(id = row_number())
Tipo de variable de cada columna
global_daily %>%
summarise_all(class) %>%
pivot_longer(everything(), names_to = "column", values_to = "type")
Podemos notar que tenemos una tablas con 784102 filas y 21 columnas.
¿Cuántos valores distintos hay por cada columna? y ¿ Cuántos NA hay en cada columna?
global_daily %>%
summarise_all(n_distinct)
global_daily %>%
summarise_all(~ sum(is.na(.x)))
Deberá llamarnos la atención que no hay correspondencia uno a uno entre conm y gvkey, por lo que suponemos que un conm tiene 2 gvkey, pues hay 292 gvkey y 291 conm Veamos…
global_daily %>%
select(gvkey, conm) %>%
group_by(conm) %>%
summarise(distinct_key = n_distinct(gvkey)) %>%
filter(distinct_key > 1)
Efectivamente es “NACIONAL FINANCIERA SNC” quien tiene asociados dos gvkey distintos. Veamos cuales son
global_daily %>%
filter(conm == "NACIONAL FINANCIERA SNC") %>%
group_by(gvkey) %>%
summarise(n = n(), prim_ap = min(datadate), ult_ap = max(datadate))
Lo que nos hace suponer que desde agosto de 2013 coexisten los dos gvkey, por lo que tendría que haber más de un registro por día
global_daily %>%
filter(conm == "NACIONAL FINANCIERA SNC") %>%
group_by(datadate) %>%
summarise(n = n()) %>%
filter(datadate >= "2013-08-07", datadate <= "2019-11-26")
Efectivamente cada día hay dos registros cada uno con diferente gvkey. Tomemos de muestra al 2019.
global_daily %>%
filter(
conm == "NACIONAL FINANCIERA SNC",
datadate >= "2013-08-07",
datadate <= "2019-11-26"
) %>%
mutate(mv = cshoc * prccd) %>% #Creamos la mv para quedarnos con la de mayor valor
group_by(datadate, conm) %>%
mutate(rank = rank(-mv)) # Creamos el ranking de mayor a menor
# Veremos que los que tienen el que gvkey que aparece menos veces son también los
# que los que tienen menor mv, así que más adelante los eliminaremos.
¡¿Qué hacemos?! ¿Quitamos los 1,645 registros de gvkey 315924? Respuesta: Hay que quitarlos…
Siguiendo con el análisis… Vemos que hay cinco diferentes monedas curcddy cinco diferentes mercados exchg. Donde el código 208 corresponde a la bolsa mexicana y que tiene 708,038 que coinciden con la suma de los registros con las monedas MXP, MXN y los NA. Así que todos los registros que no sean exchg = 208 son candidatos a eliminación
global_daily %>%
count(curcdd)
global_daily %>%
count(exchg)
global_daily %>%
count(curcdd) %>%
filter(curcdd == "MXN" | curcdd == "MXP" | is.na(curcdd)) %>%
summarise(not_eur_usd = sum(n))
global_daily %>%
count(conm) %>%
arrange(n)
# Emisoras con solo una observación y en que fecha aparece
global_daily %>%
group_by(conm) %>%
mutate(n = n()) %>%
filter(n == 1)
¿Cuántas empresas hay por sector?
# Temporalmente se usa el gsector... Que luego sabremos que en este caso es impreciso
# y tomaremos una categorización diferente
global_daily %>%
group_by(gics_name) %>%
summarise(n = n_distinct(conm))
# Se eliminan los registros que no correspondan al código de mercado de la BMV, así
# como el registro restante en USD
# CHECKPOINT
global_daily1 <- global_daily %>%
filter(exchg == 208, !curcdd %in% "USD")
# Solo quedan registros en MXP, MXN y NA
global_daily1 %>%
count(curcdd)
# Se procede a ver cuales son los registros que tiene NA en moneda, se selecciona
# el conjunto } fecha/emisora con el fin de ver si se tiene algún repetido que sí tenga moneda,
# para poder eliminar el que aparezca sin moneda
# 515 registros que no traen moneda
curcdd_na <- global_daily1 %>%
filter(is.na(curcdd)) %>%
mutate(dummy = paste(as.character(datadate), conm)) %>%
pull(dummy)
# Se selecciona los registros que sí tienen moneda y tienen un repetido sin moneda
# Se busca si alguno de los 515 registros que no tren moneda tiene algún registro de
# idéntica fecha y nombre pero que sí traiga moneda (un gemelo bueno)
rep_curr <- global_daily1 %>%
mutate(dummy = paste(as.character(datadate), conm)) %>%
filter(dummy %in% curcdd_na) %>%
filter(curcdd == "MXN") %>% # 151 **istintos** conjuntos dadtadate/conm
pull(dummy)
# Se busca en toda la base esos 151 registros, para encontrar para esa fecha y
# compañía todos los registros (con y sin moneda) y se filtran los que no tienen moneda
filter_out <- global_daily1 %>%
mutate(dummy = paste(as.character(datadate), conm)) %>%
filter(dummy %in% rep_curr & is.na(curcdd)) %>%
pull(id)
### CHECKPOINT
global_daily1 <- global_daily1 %>%
filter(!id %in% filter_out)
# ---
# ¿Cuantos NA en moneda quedan? Esos no los quitamos todavía pues no tienen un gemelo
# bueno
global_daily1 %>%
count(curcdd)
# Puesto que los que restan con NA en curcdd también tienen NA en varias columnas más
# procedo a eliminarlas
global_daily1 %>%
summarise_all(~ sum(is.na(.x)))
## CHECKPOINT
global_daily1 <- global_daily1 %>%
filter(!is.na(curcdd))
# Como se ha progresado con repecto a los NA
global_daily1 %>%
summarise_all(~ sum(is.na(.x)))
Todas las empresas que tienen alguna fecha duplicada
# Cuales son el combinado de fecha/empresa que aparecen en la base más de una vez
# y los extraemos hacia un vector
filt_dup <- global_daily1 %>%
group_by(datadate, conm) %>%
count(datadate) %>%
filter(n > 1) %>%
mutate(dummy = paste(as.character(datadate),conm)) %>%
pull(dummy)
# Se crea un dataframe con los registros dupolicados
dups <- global_daily1 %>%
mutate(dummy = paste(as.character(datadate), conm)) %>%
filter(dummy %in% filt_dup)
dups
¿Cuántas empresas diferentes tienen duplicados y cuáles son?
length(unique(dups$conm))
## [1] 72
unique(dups$conm)
## [1] "TUBOS DE ACERO DE MEXICO" "TELMEX-TELEFONOS DE MEXICO"
## [3] "WAL MART DE MEXICO SA" "INDUSTRIAS PENOLES SAB DE CV"
## [5] "KIMBERLY-CLARK DE MEXICO SA" "GRUPO MEXICO SAB DE CV"
## [7] "ALFA SAB DE CV" "GRUPO BIMBO SA DE CV"
## [9] "CEMEX SAB DE CV" "BCO NAC MEXICO"
## [11] "GRUPO INDUSTRIAL MASECA" "TELEINDUSTRIA ERICCSON SA"
## [13] "GRUPO FINANCIERO SERFIN SA" "GRUPO KUO SAB DE CV"
## [15] "GRUPO FINANCIERO BANAMEX ACC" "GRUPO TMM S.A.B"
## [17] "SYNKRO SA DE CV" "GRUPO SIDEK SA DE CV"
## [19] "GRUPO TELEVISA SAB" "GRUPO CONDUMEX SA DE CV"
## [21] "GPO FINANCIERO BBVA BANCOMER" "GRUPO CARSO SA DE CV"
## [23] "CONTROLADORA COMERCIAL MEX" "INDUSTRIAS NACOBRE SA DE CV"
## [25] "AEROVIAS DE MEXICO SA DE CV" "RASSINI SAB DE CV"
## [27] "APASCO SA DE CV" "EL PUERTO DE LIVERPOOL SA"
## [29] "ALUMSA ALUMINIO SA DE CV" "GRUPO INDUSTRIAL SALTILLO"
## [31] "GRUPO POSADAS SAB DE CV" "INTL DE CERAMICA SA DE CV"
## [33] "EMPAQUES PONDEROSA SA" "GRUPO HERDEZ SAB DE CV"
## [35] "ORGANIZACION CULTIBA SAB DE" "GRUPO MEXICANO DESARROLLO"
## [37] "PEPSI-GEMEX SA DE CV" "ACCEL SAB DE CV"
## [39] "CIA MINERA AUTLAN SA DE CV" "GRUPO IUSACELL SA"
## [41] "GPO FIN INVERMEXIC" "SAN CRISTOBAL SA"
## [43] "GRUPO FINANCIERO INBURSA SA" "SISTEMA AXIS SA"
## [45] "GRUPO FINANCIERO BANORTE SA" "GRUPO FINANCIERO BANCRECER"
## [47] "GRUPO ELEKTRA SA DE CV" "CASA DE BOLSA FINAMEX SAB DE"
## [49] "GRUPO SANBORN SA DE CV" "EDITORIAL DIANA SA DE CV"
## [51] "GRUPO PALACIO DE HIERRO" "ORBIA ADVANCE CORP SAB DE CV"
## [53] "VALORES MONTERREY AETNA SA" "REGIO EMPRESAS SA DE CV"
## [55] "MAQUINARIA DIESEL SA DE CV" "GRUPO MACMA SA DE CV"
## [57] "SOCIEDAD ELECTROMECANICA SA" "GRUPO RADIO CENTRO SA DE CV"
## [59] "FOMENTO ECONOMICO MEXICANO" "GRUPO ICONSA SA DE CV"
## [61] "INTERAMERICANA ENTRTENMIENTO" "AMERICA MOVIL SA DE CV"
## [63] "CONSORCIO G GRUPO DINA SA" "GENERAL DE SEGUROS SAB"
## [65] "HYLSAMEX SA DE CV" "CYDSA SA"
## [67] "DINE S.A.B. DE C.V." "TELMEX INTERNACIONAL SAB DE"
## [69] "BIO PAPPEL SAB DE CV" "NACIONAL FINANCIERA SNC"
## [71] "PROMOTORA Y OPERADORA INFRAE" "TELESITES SAB DE CV"
Para la limpieza de duplicados tomaremos el registro con mayor mv = cshoc * prccd Pero antes de proceder a ello tenemos que considerar los registros en que cshoc tiene NA
# Identificamos y extraemos a un vector la combinacion de fecha y empresa que tienen
# NA en cshoc para poder ver si tienen un duplicado que sí tenga un valor en cshoc
cshoc_na <- dups %>%
filter(is.na(cshoc)) %>%
pull(dummy)
dups %>%
filter(dummy %in% cshoc_na)
# Se ve que todos los registros tienen alguno igual pero sin na en cshoc, entonces se
# pueden eliminar los que tienen na
# Todos tienen al menos un gemelo bueno, entonces podemos eliminar al gemelo malo
dups %>%
filter(dummy %in% cshoc_na) %>%
group_by(datadate, conm) %>%
summarise(sin_na = sum(!is.na(cshoc)), con_na = sum(is.na(cshoc)))
elim2 <- dups %>%
filter(dummy %in% cshoc_na, is.na(cshoc)) %>%
pull(id)
## CHECKPOINT
global_daily1 <- global_daily1 %>%
filter(!id %in% elim2)
# Se actualiza el datafrane de duplicados
dups <- dups %>%
filter(!id %in% elim2)
# Ya noy hay na en cshoc en el dataframe de duplicados
dups %>%
summarise_all(~ sum(is.na(.x)))
# Creamos la nueva variable con la cual decidiremos con que registro quedarnos de los
# que estén duplicados
dups <- dups %>%
mutate(mv = cshoc * prccd)
dups
dups %>%
group_by(datadate, conm) %>%
count()
dups %>%
group_by(conm) %>%
count()
dups <- dups %>%
group_by(datadate, conm) %>%
mutate(rank = rank(-mv)) # %>%
#filter(rank > 1.5)
dups
elim3 <- dups %>%
filter(rank > 1.5) %>%
pull(id)
#### Checkpoint
global_daily1 <- global_daily1 %>%
filter(!id %in% elim3)
#####
#Los duplicados que quedan. Actualizamos dups
dups <- dups %>%
filter(rank == 1.5) # será fácil quitar los que no tengan isin
dups
dups %>%
count(datadate,conm)
dups %>%
summarise(na = sum(is.na(isin)), not_na = sum(!is.na(isin)))
elim4 <- dups %>%
filter(is.na(isin)) %>%
pull(id)
#### Checkpoint
global_daily1 <- global_daily1 %>%
filter(!id %in% elim4)
####
#Actualiza dups
vec_dups <- dups %>%
summarise(na = sum(is.na(isin)), not_na = sum(!is.na(isin))) %>%
filter(na == 0) %>%
mutate(dummy = paste(as.character(datadate), conm)) %>%
pull(dummy)
dups <- dups %>%
filter(dummy %in% vec_dups)
dups
# De estas dos empresas que quedan en duplicados, vemos que una de las variables por las que
# se diferencian es el isin, así que eliminaremos los registros que tengan el isin con menos
# repeticines en toda la base
global_daily %>%
filter(conm == "GRUPO ELEKTRA SA DE CV" | conm == "CYDSA SA") %>%
group_by(conm, isin) %>%
count()
dups %>%
group_by(conm, isin) %>%
count()
isin_elim <- global_daily %>%
filter(conm == "GRUPO ELEKTRA SA DE CV" | conm == "CYDSA SA") %>%
group_by(conm, isin) %>%
count() %>%
ungroup() %>%
top_n(-2, n) %>%
pull(isin)
## CHECKPOINT
elim5 <- dups %>%
filter(isin %in% isin_elim) %>%
pull(id)
global_daily1 <- global_daily1 %>%
filter(!id %in% elim5)
####
# Ya no existen duplicados de una misma empresa con fechas repetidas en la base
dups <- get_dupes(global_daily1, datadate, conm)
## No duplicate combinations found of: datadate, conm
global_daily1 %>%
count(conm) %>%
arrange(n)
global_daily1 %>%
mutate(year = year(datadate)) %>%
group_by(conm, year) %>%
summarise(n = n()) %>%
spread(year, n)
global_daily1 %>%
summarise_all(~ sum(is.na(.x)))
## CHECKPOINT
# Se eliminaran por este momento aquellas empresas que tengan menos de 60 registros
conm_men60 <- global_daily1 %>%
count(conm) %>%
arrange(n) %>%
filter(n < 60) %>%
pull(conm)
elim6 <- global_daily1 %>%
filter(conm %in% conm_men60) %>%
pull(id)
## CHECKPOINT
global_daily1 <- global_daily1 %>%
filter(!id %in% elim6)
global_daily1 %>%
distinct(conm, gics_name)
global_daily1 %>% mutate(year = year(datadate)) %>% group_by(conm, year) %>% summarise(n = sum(monthend)) %>% spread(year, n)
global_daily1 %>%
filter(monthend == 1) %>%
select(datadate, conm, monthend) %>%
mutate(dia = day(datadate)) %>%
filter(dia >= 5 & dia < 25)
global_daily1 %>%
mutate(mes = month(datadate), year = year(datadate)) %>%
filter(
conm == "INDUSTRIAS NACOBRE SA DE CV",
year == 1988 | year == 1987
#mes == 1 | mes == 2
) %>%
select(datadate, conm, monthend)
global_daily1 %>%
mutate(year = year(datadate)) %>%
group_by(gics_name, year) %>%
summarise(n = n_distinct(conm)) %>%
spread(year, n)
Esta es otra prueba. Es para recordar lo que ya hicimos.
Prueba 3. A comer.
Prueba 4. Rober quiere borrar sólo una línea. Esta es la segunda línea. Y esta es la tercera.
Prueba 5. Una tercera fila de datos.